package com.nervenets.web.redis;

import com.nervenets.aspect.RedisAsyncLocker;
import com.nervenets.utils.JodaUtils;
import com.nervenets.web.enumeration.OperateType;
import com.nervenets.web.enumeration.OrderStatus;
import com.nervenets.web.enumeration.Time;
import com.nervenets.web.enumeration.WealthType;
import com.nervenets.web.hibernate.dao.UserDao;
import com.nervenets.web.hibernate.domain.film.Movie;
import com.nervenets.web.hibernate.domain.user.Order;
import com.nervenets.web.hibernate.domain.user.User;
import com.nervenets.web.redis.domain.movie.MovieStored;
import com.nervenets.web.redis.domain.user.*;
import com.nervenets.web.redis.domain.global.GlobalUserCount;
import com.nervenets.web.redis.utils.RedisSyncLocker;
import org.springframework.stereotype.Service;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisCluster;
import redis.clients.jedis.JedisPool;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
 * Created by Joe on 2018/1/5.
 */
@Service
public class GlobalService {
    @Resource
    private RedisDao redisDao;
    @Resource
    private RedisSetDao redisSetDao;
    @Resource
    private RedisOrderedSetDao redisOrderedSetDao;
    @Resource
    private UserDao userDao;
    @Resource
    private RedisSyncLocker redisSyncLocker;

    public UserStored getUser(long userId) {
        UserStored us = new UserStored(userId);
        if (redisSetDao.exists(us)) {
            return redisSetDao.getRedisSetDomain(us);
        } else {
            User user = userDao.findById(userId, User.class);
            if (null != user) {
                us = new UserStored(user);
                redisSetDao.saveOrUpdate(us);
                return us;
            }
        }
        return null;
    }

    public void clearRedisUserStored(long userId) {
        UserStored usSelect = new UserStored(userId);
        redisSetDao.delete(usSelect);
    }

    public MovieStored getMovie(long movieId) {
        MovieStored ms = new MovieStored(movieId);
        if (redisSetDao.exists(ms)) {
            return redisSetDao.getRedisSetDomain(ms);
        } else {
            final Movie movie = userDao.findById(movieId, Movie.class);
            if (null != movie) {
                ms = new MovieStored(movie);
                redisSetDao.saveOrUpdate(ms);
                return ms;
            }
        }
        return null;
    }

    public void clearRedisMovieStored(long movieId) {
        MovieStored ms = new MovieStored(movieId);
        redisSetDao.delete(ms);
    }

    public long getUserWealthOfMoney(long userId, WealthType wealthType) {
        WealthOfMoney wealthOfMoney = new WealthOfMoney(userId, wealthType);
        Double zScore = redisOrderedSetDao.getZScore(wealthOfMoney);
        if (null == zScore) {
            long surplus = userDao.getSurplus(userId, wealthType);
            zScore = redisOrderedSetDao.incrBy(wealthOfMoney, surplus);
        }
        return zScore.longValue();
    }

    public long addUserWealthOfMoney(long userId, WealthType wealthType, long offset) {
        WealthOfMoney wealthOfMoney = new WealthOfMoney(userId, wealthType);
        Double zScore = redisOrderedSetDao.getZScore(wealthOfMoney);
        if (null == zScore) {
            long surplus = userDao.getSurplus(userId, wealthType);
            redisOrderedSetDao.incrBy(wealthOfMoney, surplus);
        }
        zScore = redisOrderedSetDao.incrBy(wealthOfMoney, offset);
        return zScore.longValue();
    }

    public void removeRedisWealthOfMoney(long userId, WealthType wealthType) {
        WealthOfMoney wealthOfMoney = new WealthOfMoney(userId, wealthType);
        redisOrderedSetDao.delete(wealthOfMoney);
    }

    public long getUserWealthOfTotalMoney(long userId, WealthType wealthType) {
        WealthOfTotalMoney wealthOfTotalMoney = new WealthOfTotalMoney(userId, wealthType);
        Double zScore = redisOrderedSetDao.getZScore(wealthOfTotalMoney);
        if (null == zScore) {
            long totalGet = userDao.getTotalGet(userId, wealthType);
            zScore = redisOrderedSetDao.incrBy(wealthOfTotalMoney, totalGet);
        }
        return zScore.longValue();
    }

    public long addUserWealthOfTotalMoney(long userId, WealthType wealthType, long offset) {
        if (offset <= 0) return getUserWealthOfMoney(userId, wealthType);
        WealthOfTotalMoney wealthOfTotalMoney = new WealthOfTotalMoney(userId, wealthType);
        Double zScore = redisOrderedSetDao.getZScore(wealthOfTotalMoney);
        if (null == zScore) {
            long totalGet = userDao.getTotalGet(userId, wealthType);
            redisOrderedSetDao.incrBy(wealthOfTotalMoney, totalGet);
        }
        zScore = redisOrderedSetDao.incrBy(wealthOfTotalMoney, offset);
        return zScore.longValue();
    }

    public void removeRedisWealthOfTotalMoney(long userId, WealthType wealthType) {
        WealthOfTotalMoney wealthOfTotalMoney = new WealthOfTotalMoney(userId, wealthType);
        redisOrderedSetDao.delete(wealthOfTotalMoney);
    }

    public void addUserWealthOfMoneyByOperateType(long userId, WealthType wealthType, OperateType operateType, long offset) {
        WealthOfMoneyByOperateType wealthOfMoneyByOperateType = new WealthOfMoneyByOperateType(userId, operateType, wealthType);
        if (redisSetDao.exists(wealthOfMoneyByOperateType)) {
            wealthOfMoneyByOperateType = redisSetDao.getRedisSetDomain(wealthOfMoneyByOperateType);
        }
        wealthOfMoneyByOperateType.add(offset);
        redisSetDao.saveOrUpdate(wealthOfMoneyByOperateType);
    }

    public WealthOfMoneyByOperateType getUserWealthOfMoneyByOperateType(long userId, OperateType operateType, WealthType wealthType) {
        WealthOfMoneyByOperateType wealthOfMoneyByOperateType = new WealthOfMoneyByOperateType(userId, operateType, wealthType);
        if (redisSetDao.exists(wealthOfMoneyByOperateType)) {
            wealthOfMoneyByOperateType = redisSetDao.getRedisSetDomain(wealthOfMoneyByOperateType);
        }
        return wealthOfMoneyByOperateType;
    }

    public long addMovieBuyMoney(long movieId, long offset) {
        MovieBuyMoney movieBuyMoney = new MovieBuyMoney();
        movieBuyMoney.setMovieId(movieId);
        Double zScore = redisOrderedSetDao.getZScore(movieBuyMoney);
        if (null == zScore) {
            long totalBuy = 0;
            List<Object> params = new ArrayList<>();
            params.add(movieId);
            params.add(OrderStatus.paid);
            params.add(OrderStatus.finish);
            List<Order> orders = userDao.getAllObjectsByHql("from Order where unionId = ? and status between ? and ? ", params);
            for (Order order : orders) {
                totalBuy += order.getExtra().getNumber() * order.getExtra().getPriceOfUnit();
            }
            zScore = redisOrderedSetDao.incrBy(movieBuyMoney, totalBuy);
        } else {
            zScore = redisOrderedSetDao.incrBy(movieBuyMoney, offset);
        }
        return zScore.longValue();
    }

    public long getMovieBuyMoney(long movieId) {
        MovieBuyMoney movieBuyMoney = new MovieBuyMoney();
        movieBuyMoney.setMovieId(movieId);
        Double zScore = redisOrderedSetDao.getZScore(movieBuyMoney);
        if (null == zScore) {
            long totalBuy = 0;
            List<Object> params = new ArrayList<>();
            params.add(movieId);
            params.add(OrderStatus.paid);
            params.add(OrderStatus.finish);
            List<Order> orders = userDao.getAllObjectsByHql("from Order where unionId = ? and status between ? and ? ", params);
            for (Order order : orders) {
                totalBuy += order.getExtra().getNumber() * order.getExtra().getPriceOfUnit();
            }
            zScore = redisOrderedSetDao.incrBy(movieBuyMoney, totalBuy);
        }
        return zScore.longValue();
    }

    @RedisAsyncLocker(key = "GlobalService.activeUser")
    public void activeUser(long userId) {
        UserStored user = getUser(userId);
        if (null != user) {
            GlobalUserCount userCount = new GlobalUserCount(Time.hour, JodaUtils.getStartOfThisHour());
            if (redisDao.exist(userCount.getRedisKey())) {
                userCount = redisDao.getRedisDomain(userCount.getRedisKey(), GlobalUserCount.class);
            }
            userCount.addUser(user);
            redisDao.saveOrUpdate(userCount);
        }
    }

    /**
     * 设置全局键值对
     *
     * @param key   键
     * @param value 值
     * @throws Exception
     */
    public void setGlobalPair(String key, String value) {
        redisDao.addString(key, value);
    }

    public void setGlobalPair(String key, String value, int expire) {
        redisDao.addString(key, value, expire);
    }

    /**
     * 获取全局键值对
     *
     * @param key 键
     * @return 值
     * @throws Exception
     */
    public String getGlobalPair(String key) throws Exception {
        return redisDao.getString(key);
    }

    public long getGlobalPairExpired(String key) throws Exception {
        return redisDao.getExpired(key);
    }

    public void clearRedis() {
        try {
            final JedisCluster cluster = (JedisCluster) redisDao.getCluster();
            Map<String, JedisPool> clusterNodes = cluster.getClusterNodes();
            for (String key : clusterNodes.keySet()) {
                System.out.println(String.format("clear redis %s ...  ", key));
                final JedisPool jedisPool = clusterNodes.get(key);
                final Jedis jedis = jedisPool.getResource();
                jedis.flushDB();
                jedis.flushAll();
            }
        } catch (Exception e) {
            System.out.println(e.getMessage());
        }
    }
}
